package com.sunshine.wrongquestionset.utils

import android.annotation.SuppressLint
import com.github.mikephil.charting.data.BarEntry
import com.github.mikephil.charting.data.Entry
import com.github.mikephil.charting.data.PieEntry
import com.sunshine.wrongquestionset.room.entity.QuestionEntity
import com.sunshine.wrongquestionset.room.entity.QuestionInfoEntity
import com.sunshine.wrongquestionset.room.entity.QuestionTypeEntity
import com.sunshine.wrongquestionset.room.entity.WrongQuestionBean
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
import kotlin.math.max
import kotlin.math.min

/**
 * @author sunshine
 * @date 2021/4/14
 * 分析工具类
 */
object AnalyzerUtils {

    //遗忘曲线天数数组
    private val forgottenArray = arrayOf(1, 2, 4, 7, 15, 30)

    /**
     * 获取过去指定时间内指定错题集合的每天练习数量
     * @param list 错题集合
     * @param day  获取多长时间之内的统计信息
     * @return     一个可以供柱状图使用的数据集合
     */
    @SuppressLint("SimpleDateFormat")
    fun getLastDayExerciseSituation(list: List<WrongQuestionBean>?, day: Int): LastDayExerciseSituationBean<BarEntry> {
        //在指定日期内的做题数量
        val nowDate = TimeUtils.getNowTime()

        val xTimeMap = HashMap<Int, String>()
        val simpleDateFormat = SimpleDateFormat("M/d")
        var maxY = 0

        //存储某天做题数量的表，key，某天，value，数量
        val exerciseCountInDayMap = HashMap<Int, Int>()
        for (i in 0..day) {
            exerciseCountInDayMap[i] = 0
            //day - i可以让数据倒着显示
            xTimeMap[day - i] = simpleDateFormat.format(Date(nowDate - i * TimeUtils.DAY_LONG))
        }

        list?.forEach {wrongQuestionBean ->
            val dayDifference = TimeUtils.dateDifference(wrongQuestionBean.date, nowDate)
            //day - i可以让数据倒着显示
            if (exerciseCountInDayMap[day - dayDifference] != null) {
                exerciseCountInDayMap[day - dayDifference] = exerciseCountInDayMap[day - dayDifference]!!.plus(1)
                maxY = maxY.coerceAtLeast(exerciseCountInDayMap[day - dayDifference]!!)
            }

        }

        val entryList = ArrayList<BarEntry>()
        exerciseCountInDayMap.forEach { (aDay, count) ->
            entryList.add(BarEntry(aDay.toFloat(), count.toFloat()))
        }

        return LastDayExerciseSituationBean(entryList, xTimeMap, maxY.toFloat())
    }

    /**
     * 只保留同一道题在同一天的最新记录
     */
    private fun removeSameQuestionInfo(
        list: List<QuestionInfoEntity>?,
        nowDate: Long
    ): ArrayList<QuestionInfoEntity>? {
        if (list.isNullOrEmpty()) return null

        var questionId = -1
        var differenceDay = -1

        val infoList = ArrayList<QuestionInfoEntity>()
        for (i in list.size - 1 downTo 0) {
            val thisDifferenceDay = TimeUtils.dateDifference(list[i].time, nowDate)
            if (differenceDay != thisDifferenceDay || (differenceDay == thisDifferenceDay && list[i].questionId != questionId)) {
                infoList.add(list[i])
                questionId = list[i].questionId
                differenceDay = thisDifferenceDay
            }
        }
        return infoList
    }

    /**
     * 获取过去指定时间内指定题目集合的每天给定列表熟悉程度的变化
     */
    @SuppressLint("SimpleDateFormat")
    fun getLastDayExerciseFamiliaritySituation(list: List<QuestionInfoEntity>?, day: Int): LastDayExerciseSituationBean<Entry> {
        //在指定日期内的做题数量
        val nowDate = TimeUtils.getNowTime()

        val removeList = removeSameQuestionInfo(list, nowDate)

        val xTimeMap = HashMap<Int, String>()
        val simpleDateFormat = SimpleDateFormat("M/d")
        var maxY = 0.0f

        //存储某天做题数量的表，key，某天，value，熟悉程度
        val familiarityInDayMap = HashMap<Int, Float>()
        //储存某天做题数量的个数
        val familiarityInDayCount = HashMap<Int, Int>()

        for (i in 0..day) {
            familiarityInDayMap[i] = 0.0f
            familiarityInDayCount[i] = 0
            //day - i可以让数据倒着显示
            xTimeMap[day - i] = simpleDateFormat.format(Date(nowDate - i * TimeUtils.DAY_LONG))
        }


        removeList?.forEach {questionInfoEntity ->
            val dayDifference = TimeUtils.dateDifference(questionInfoEntity.time, nowDate)
            //day - i可以让数据倒着显示
            if (familiarityInDayMap[day - dayDifference] != null) {
                familiarityInDayMap[day - dayDifference] = familiarityInDayMap[day - dayDifference]!!.plus(questionInfoEntity.familiarity)
                familiarityInDayCount[day - dayDifference] = familiarityInDayCount[day - dayDifference]?.plus(1)?:0
                maxY = maxY.coerceAtLeast(familiarityInDayMap[day - dayDifference]!!)
            }
        }

        val entryList = ArrayList<Entry>()
        familiarityInDayMap.forEach { (aDay, familiarity) ->
            var count = familiarityInDayCount[aDay]?:0
            count = if (count == 0) 1 else count
            //让显示的熟悉度在0-1之间
            entryList.add(Entry(aDay.toFloat(), max(min(familiarity / count.toFloat(), 1.0f), 0.0f)))
        }

        return LastDayExerciseSituationBean(entryList, xTimeMap, maxY)
    }

    /**
     * 获取给定列表中题目组成成分
     * 选择，填空等
     */
    fun getWrongQuestionTypePart(list: List<QuestionEntity>?, typeList: List<QuestionTypeEntity>?): ArrayList<PieEntry> {
        //存储每个题目类型的数量
        val wrongQuestionTypeCountMap = HashMap<Int, Int>()
        list?.forEach {
            wrongQuestionTypeCountMap[it.typeId] = wrongQuestionTypeCountMap[it.typeId]?.plus(1)?:1
        }

        val pieEntryList = ArrayList<PieEntry>()
        //将题目id和类型名称对应
        val typeMap = HashMap<Int, String>()
        typeList?.forEach {
            typeMap[it.id] = it.name
        }

        wrongQuestionTypeCountMap.forEach { (typeId, count) ->
            pieEntryList.add(PieEntry(count.toFloat(), typeMap[typeId]))
        }

        return pieEntryList
    }

    /**
     * 获取给定列表中题目熟悉程度分布
     * 选择，填空等
     */
    fun getQuestionDistribution(list: List<QuestionEntity>?): ArrayList<PieEntry> {
        //存储每个题目熟悉程度分布
        val questionDistributionCountMap = HashMap<String, Int>()
        list?.forEach {
            val familiarityType: String
            when (it.familiarity) {
                in -1.0f .. 0.2f -> {
                    familiarityType = "0%-20%"
                }
                in 0.2f .. 0.4f -> {
                    familiarityType = "20%-40%"
                }
                in 0.4f .. 0.6f -> {
                    familiarityType = "40%-60%"
                }
                in 0.6f .. 0.8f -> {
                    familiarityType = "60%-80%"
                }
                else -> {
                    familiarityType = "80%-100%"
                }
            }
            questionDistributionCountMap[familiarityType] = questionDistributionCountMap[familiarityType]?.plus(1)?:1
        }

        val pieEntryList = ArrayList<PieEntry>()

        questionDistributionCountMap.forEach { (type, count) ->
            pieEntryList.add(PieEntry(count.toFloat(), type))
        }

        return pieEntryList
    }

    /**
     * 给定一个表，选择推荐的科目
     * @param questionMap 问题表 key 科目id value 该科目所有题目
     * @return 最推荐的题目
     *
     * @see getQuicklyExerciseQuestionList
     */
    fun getBestQuicklyExerciseSubject(questionMap: HashMap<Int, ArrayList<QuestionEntity>>): QuicklyExerciseBean? {
        if (questionMap.isNullOrEmpty()) return null

        var bestSubjectId = -1
        var bestFamiliarity = Float.MAX_VALUE
        var bestQuestionList: List<QuestionEntity>? = null
        questionMap.forEach { (subjectId, questionList) ->
            val questionListOnce = getQuicklyExerciseQuestionList(questionList)

            //单科推荐的题目不为空，才会推荐
            if (!questionListOnce.isNullOrEmpty()) {
                var familiarityOnce = 0.0f
                questionListOnce.forEach {
                    familiarityOnce += it.familiarity
                }

                if (familiarityOnce / questionListOnce.size < bestFamiliarity) {
                    bestSubjectId = subjectId
                    bestFamiliarity = familiarityOnce / questionListOnce.size
                    bestQuestionList = questionListOnce
                }
            }
        }

        return QuicklyExerciseBean(bestSubjectId, bestQuestionList)
    }

    /**
     * 获取智能练习推荐的题目
     *
     * 首先按照熟悉程度对列表升序排列
     *
     * 算法：首选根据遗忘曲线推荐
     * 如果超过用户设置的一次练习题目数量，则取前几个
     *
     * 但是，我们还要考虑的是，应该让每次提供的题目都是一科，所以需要挑选最需要练习的一科，
     * 而选择最需要的，是要选择需要练习的科目中需要练习题目的熟悉程度平均值
     * 但是需要注意的是，这里传过来的 questionList，就是单科的，需要调用者通过一系列返回值自行选择
     *
     * 如果最终选择的题目数量不够，我们仍然不能选择同时练习不同科目，而是取其次练习：熟悉程度较低的题目
     * @param questionList 题目列表
     * //@param exerciseCount 每次练习最大个数
     * @return 本科推荐的题目
     */
    private fun getQuicklyExerciseQuestionList(questionList: List<QuestionEntity>?): ArrayList<QuestionEntity>? {
        if (questionList.isNullOrEmpty()) return null
        else {
            //现在的时间
            val nowTime = TimeUtils.getNowTime()

            //按照熟悉程度降序排列
            Collections.sort(questionList, CompareFamiliarity())

            //匹配的题目
            val matchQuestionList = ArrayList<QuestionEntity>()

            //4 17更改，先插入前些天没有按照遗忘曲线练习的题目
            questionList.forEach {
                //今天距离首天的天数
                val dayDifference = TimeUtils.dateDifference(it.insertTime, nowTime)
                val lastDifference = TimeUtils.dateDifference(it.lastTime, nowTime)
                val doCount = it.doCount
                //实际应该做几次
                var needTodoCount = 0

                var needTodoDay = 0

                //比较当前天数在哪个遗忘曲线范围内，比如3，就应该在2-4之间，那么今天之前应该做了2次才对
                //1 2 4
                for (i in forgottenArray.indices) {
                    if (dayDifference < forgottenArray[i]) {
                        needTodoCount = i
                        if (i > 0) needTodoDay = forgottenArray[i - 1]
                        break
                    }
                }

                //如果做的次数比应该做的少，就提前
                //lastDifference：今天做完了就不做了
                if (lastDifference > 0 && doCount < needTodoCount && !matchQuestionList.contains(it)) {
                    //更改题目插入时间，表示对遗忘曲线改变了
                    it.insertTime += (dayDifference - needTodoDay) * TimeUtils.DAY_LONG
                    matchQuestionList.add(it)
                }
            }

            //println(matchQuestionList)

            //寻找列表中在遗忘曲线天数的题目
            //4/17更改，采用insertTime
            questionList.forEach {
                val dayDifference = TimeUtils.dateDifference(it.insertTime, nowTime)
                val lastDifference = TimeUtils.dateDifference(it.lastTime, nowTime)
                //lastDifference：今天做完了就不做了
                if (lastDifference > 0 && forgottenArray.contains(dayDifference) && !matchQuestionList.contains(it)) {
                    matchQuestionList.add(it)
                }
            }

            //按照老师的说法，智能推荐只应该推荐遗忘曲线天数的
            return matchQuestionList

//            //如果当前就够了，则返回，返回的列表是按照熟悉程度升序的，所以如果比练习数量多，那么取前几个就行
//            if (matchQuestionList.size >= exerciseCount) return matchQuestionList
//
//            //推荐匹配列表没有的，且熟悉度较低的
//            questionList.forEach {
//                if (!matchQuestionList.contains(it)) {
//                    matchQuestionList.add(it)
//                }
//                if (matchQuestionList.size >= exerciseCount) return@forEach
//            }
//
//            return matchQuestionList
        }
    }

    /**
     * 按照熟悉程度升序排序列表
     */
    fun sortQuestionListByFamiliarity(list: List<QuestionEntity>?): List<QuestionEntity>? {
        Collections.sort(list, CompareFamiliarity())
        return list
    }

    /**
     * 分析一道题的熟悉程度变化
     * 这里单次分析不是一次错题记录，而是一次题目记录
     * 这样，即使每天不练习，但是随着时间变化，题目的熟悉程度应该也是有所下降的，这样，这里也能体现出来
     * @param count 显示最近count条记录
     */
    @SuppressLint("SimpleDateFormat")
    fun analyzerQuestionFamiliarity(questionInfoList: List<QuestionInfoEntity>?, count: Int): QuestionFamiliarityBean? {
        if (questionInfoList.isNullOrEmpty()) return null

        val simpleDateFormat = SimpleDateFormat("M/d")

        Collections.sort(questionInfoList, CompareDate())

        //当前已经添加的
        var i = 0

        //返回的列表长度
        val maxCount = min(count, questionInfoList.size)

        val entryList = ArrayList<Entry>()
        val xTimeMap = HashMap<Int, String>()

        questionInfoList.forEach {
            if (i >= count) return@forEach

            //添加一条
            entryList.add(Entry((maxCount - i).toFloat(), max(0.0f, min(1.0f, it.familiarity))))

            xTimeMap[maxCount - i] = simpleDateFormat.format(Date(it.time))

            i++
        }

        entryList.reverse()

        return QuestionFamiliarityBean(entryList, xTimeMap)
    }

    /**
     * 按照熟悉程度升序排序问题列表
     */
    class CompareFamiliarity : Comparator<QuestionEntity>{
        override fun compare(o1: QuestionEntity?, o2: QuestionEntity?): Int {
            //注意排序不能toInt()
            val familiarityDifference = (o1?.familiarity?:0.0f) - (o2?.familiarity?:0.0f)
            return if (familiarityDifference > 0) 1 else -1
        }
    }

    /**
     * 按照时间降序排列问题信息
     */
    class CompareDate : Comparator<QuestionInfoEntity> {
        override fun compare(o1: QuestionInfoEntity?, o2: QuestionInfoEntity?): Int {
            return ((o2?.time ?: 0L) - (o1?.time ?: 0L)).toInt()
        }

    }
}